#include <signal.h>
#include <XrdDownloadManagerLog.hh>

#include "XrdDownloadManagerAliEn.hh"

XrdDownloadManagerAliEn::XrdDownloadManagerAliEn() : XrdDownloadManagerBase(),
        fRenewCertificateTime ( "1:00" ),
        fCertificateDir ( "" ),
        fAliEnUserName ( "proof" ),
        fXrdgsiproxyCmd ( ALIEN_XRDGSIPROXY ),
        fAliEnTokenInitCmd ( ALIEN_ALIEN_TOKEN_INIT ),
        fAliEnTokenDestroyCmd ( ALIEN_ALIEN_TOKEN_DESTROY ),
        fAliEnTokenInfoCmd( ALIEN_ALIEN_TOKEN_INFO ),
        fAliEnCpCmd ( ALIEN_ALIEN_CP ),
        fAliEnTimeout ( 120 ),
        fAliEn ( 0 ),
        fIsAliEnConnected ( false )
//
{
    fSourceTypes = "aliengr,alien,aliencp";
//     DoAuthenticate(true);

}
XrdDownloadManagerAliEn::~XrdDownloadManagerAliEn() {
}

int XrdDownloadManagerAliEn::DoAuthenticate ( bool showProcess ) {

    fDoAuthenticate = true;
    XrdDMLogDebugFull ( "AliEn DoAuthenticate(%d)",fDoAuthenticate );

    int retVal=0;

    if ( fDoAuthenticate ) {
        XrdDMLogDebugFull ( "AliEn DoAliEnTokenInit(%d)",showProcess );
        retVal =  DoAliEnTokenInit ( showProcess );
        if ( retVal ) {
            string tmpStrInfo = "xrddm [";
            tmpStrInfo.append(fFileNameFrom.data());
            tmpStrInfo.append("] AliEn token is FAILED !!! ");
            XrdDMLogGError ( tmpStrInfo.data());
            XrdDMLogGError ( "Error DoAuthenticate::DoAliEnTokenInit !!! " );
            return 1;
        } else {

            string tmpStrInfo = "xrddm [";
            tmpStrInfo.append(fFileNameFrom.data());
            tmpStrInfo.append("] AliEn token is OK !!! ");
            XrdDMLogGInfo ( tmpStrInfo.data());
        }
    }

    DoSetAliEnVariables();

//     if (ConnectToAliEn()) return 3;
    // it will be handled by fIsAliEnConnected variable
    ConnectToAliEn();
    return retVal;
}

int XrdDownloadManagerAliEn::DoCopyPre ( string from, string to ) {

    // Authenticate if needed
    if ( DoAuthenticate ( true ) ) return 2;

    if ( ( from.empty() ) || ( to.empty() ) ) return 0;

    return 0;
}

int XrdDownloadManagerAliEn::DoCopyWithType ( XrdDownloadManagerBase::EInputTypes_t type, string from, string to ) {

    int retFill = 0;
    switch ( type ) {
    case kStdXrdcpAliEnWhereis :
        if ( !fIsAliEnConnected ) return 3;
        retFill = FillListOfFileNamesFrom ( from );
        if ( retFill > 100) return retFill;
        XrdDMLogDebugFull ( "DoCopyStd %d %s %s ",type,from.c_str(),to.c_str() );
        return DoCopyStd ( from,to );
    case kStdXrdcpAliEnWhereisGR :
        if ( !fIsAliEnConnected ) return 3;
        retFill = FillListOfFileNamesFrom ( from );
        if ( retFill > 100 ) return retFill;
        XrdDMLogDebugFull ( "DoCopyStd %d %s %s ",type,from.c_str(),to.c_str() );
        return DoCopyStdGR ( from ,to );
    case kStdAliEnCp :
        XrdDMLogDebugFull ( "DoAliEnCp %d %s %s ",type,from.c_str(),to.c_str() );
        return DoAliEnCp ( from,to );
    default:
        return XrdDownloadManagerBase::DoCopyWithType ( type,from,to );
    }

    return 1;
}


int XrdDownloadManagerAliEn::DoCopyPost ( string from, string to ) {

    return XrdDownloadManagerBase::DoCopyPost ( from, to );
}

int XrdDownloadManagerAliEn::FillListOfFileNamesFrom ( string fname ) {
//  XrdDownloadManagerBase::FillListOfFileNamesFrom(fname);

    return FillListOfFileNamesFromAliEnUsingWhereis ( fname );
}

int XrdDownloadManagerAliEn::FillListOfFileNamesFromAliEnUsingWhereis ( string fname ) {


    if ( fListOfFileNamesFrom.size() >0 ) {
        XrdDMLogDebugFull ( "fListOfFileNamesFrom already filled with size %d. Skipping...",fListOfFileNamesFrom.size() );
        return fListOfFileNamesFrom.size();
    }

    if ( !fAliEn ) {
        XrdDMLogError ( "fAliEn is null!!!! Skipping..." );
        return 0;
    }
    if ( !fIsAliEnConnected ) {
        XrdDMLogError ( "No AliEn connection available!!!!! Skipping..." );
        return 0;
    }

    string alienfilename = GetXrdFilename ( fname.c_str() );
    string cmd ( "whereis " );
    cmd.append ( alienfilename );
//     fCurrentFileNameNice = filename.c_str();
    XrdDMLogDebugFull ( "Doing '%s' ...",cmd.c_str() );
    bool retValWhereis = true;
//     signal ( SIGALRM,CallAliEnTimeout );
//     alarm ( fAliEnTimeout );
    try {

        retValWhereis = fAliEn->Command ( cmd.c_str() );
//         alarm (0);
    } catch ( int e ) {
        XrdDMLogGError ( "fAliEn->Command(%s) time out %d !!! Exiting ...",cmd.c_str(),e );
        XrdDMLogGError ( "Error in AliEn !!! " );
        return 0;
    }

    XrdDMLogDebugFull ( "Doing '%s' done with %d...",cmd.c_str(),retValWhereis );

    int numberOfReplicas = fAliEn->GetStreamColumns ( 2 );

    if (numberOfReplicas<1) {
        // file was not found in catalogue
        string str = "xrddm [";
        str.append(fname.c_str());
        str.append("] No replicas in AliEn catalogue. File not found");
        XrdDMLogGError ( str.data());
        return 101;
    }

    string currFileStr,grpath;
    XrdDMLogDebugFull ( "Number of replicas %d in AliEn catalogue ...",numberOfReplicas );
    for ( int i = 0;i < numberOfReplicas;i++ ) {
        currFileStr = fAliEn->GetStreamFieldValue ( 2, i, 2 );
        XrdDMLogDebugFull ( "Found Path in AliEn catalogue GR %s ...",currFileStr.c_str() );
        if ( !currFileStr.compare ( 0,7,"guid://" ) ) {
            XrdDMLogWarning ( "%s starts with 'guid://' !!! Skipping ...",currFileStr.c_str() );
            continue;
        }
        if ( ( !i ) && ( !fGlobalRedirectorName.empty() ) ) {
            grpath = GetXrdGRPath ( currFileStr ).c_str();
            XrdDMLogDebugFull ( "Adding GR %s ...",grpath.c_str() );
            fListOfFileNamesFromGR.push_back ( grpath );
//             break;
        }
        XrdDMLogDebugFull ( "Adding %s ...",currFileStr.c_str() );
        fListOfFileNamesFrom.push_back ( currFileStr );
    }
    return numberOfReplicas;
}

int XrdDownloadManagerAliEn::ConnectToAliEn() {

    if ( fIsAliEnConnected ) {
        XrdDMLogWarning ( "Already connected to AliEn. Skipping  ..." );
        return 0;
    }

    XrdDMLogDebugSilent ( "Connecting to AliEn ..." );

    if ( !fAliEn ) {
        XrdDMLogDebugSilent ( "Creating GapiUI... " );
        fAliEn = GapiUI::MakeGapiUI ( true );
    } else {
        XrdDMLogDebugSilent ( "Reusing GapiUI... " );
    }

    if ( !fAliEn ) {
        XrdDMLogGError ( "Error creating GapiUI !!! " );
        fIsAliEnConnected = false;
        return 1;
    }
    XrdDMLogDebugSilent ( "GapiUI is OK ... " );
    string alienHost,alienPort,alienUser;


//     XrdDMLogDebugSilent ( "Test ... " );
    alienHost = GetEnvValue ( "alien_API_HOST" );

//     XrdDMLogDebugSilent ( "Test ... " );
    alienPort = GetEnvValue ( "alien_API_PORT" );
//     XrdDMLogDebugSilent ( "Test ... " );
    alienUser = GetEnvValue ( "alien_API_USER" );

//     XrdDMLogDebugSilent ( "Test ... " );
//     XrdDMLogGInfo ( "alien_API_HOST=%s",alienHost.c_str() );
//     XrdDMLogGInfo ( "alien_API_PORT=%s",alienPort.c_str() );
//     XrdDMLogGInfo ( "alien_API_USER=%s",alienPort.c_str() );

// alienHost(fAliEnUserName);

//     if ((alienHost.empty())||(alienPort.empty())||(alienPort.empty())) {
//         XrdDMLogGError ( "\talien_API_HOST=%s",alienHost.c_str() );
//         XrdDMLogGError ( "\talien_API_PORT=%s",alienPort.c_str() );
//         XrdDMLogGError ( "\talien_API_USER=%s",alienPort.c_str() );
//         XrdDMLogGError ( "Error in AliEn Variables !!! " );
//         return 2;
//     }

    XrdDMLogDebugSilent ( "AliEn variables OK ... " );
    XrdDMLogDebugSilent ( "Doing  fAliEn->Connect(\"\",0,%s,0) ...", fAliEnUserName.c_str() );
//     fAliEn->Connect ( alienHost.c_str(), atoi ( alienPort.c_str() ), alienUser.c_str(), "0" );

    signal ( SIGALRM,&CallAliEnTimeout );
    alarm ( fAliEnTimeout );
    try {
        XrdDMLogInfo ( "Connecting to AliEn ..." );
        fAliEn->Connect ( "",0,fAliEnUserName.c_str(),0 );
//         XrdDMLogGInfo ( "Sleeping %d ...",fAliEnTimeout+100);
//         sleep(fAliEnTimeout+100);
//         XrdDMLogGInfo ( "fAliEn->Connect ...");
        alarm ( 0 );
        fIsAliEnConnected = true;
    } catch ( int e ) {
        XrdDMLogGError ( "fAliEn->Connect time out %d !!! Exiting ...",e );
        XrdDMLogGError ( "Error connecting to AliEn !!! " );
        fIsAliEnConnected = false;
        return 1;
    }


    if ( !fAliEn->Connected() ) {
        XrdDMLogGError ( "Error connecting to AliEn (fAliEn->Connected()) !!! " );
        fIsAliEnConnected = false;
        return 1;
    } else {
        fIsAliEnConnected = true;
    }
    XrdDMLogDebugSilent ( "Connecting to AliEn is successful..." );
    return 0;
}


string XrdDownloadManagerAliEn::GetEnvValue ( string env ) {
    char * tmpenvvar;
    tmpenvvar = getenv ( env.c_str() );
    if ( tmpenvvar!=NULL )
        return string ( tmpenvvar );
    else
        return "";
}


int XrdDownloadManagerAliEn::DoAliEnTokenInit ( bool showProcess ) {


    XrdDMLogDebugFull ( "DoAliEnTokenInit(%d) ...",showProcess );
    if ( !IsProxyValid(showProcess) ) {
        XrdDMLogInfo ( "Renewing Grid proxy..." );
        XrdDMLogGInfo ( "Renewing Grid proxy ..." );
        TokenDestroy ( showProcess );
        if ( ProxyInit ( showProcess ) ) {
            XrdDMLogInfo ( "Grid proxy renewed successfully" );
        } else {
            XrdDMLogError ( "Cannot renew Grid proxy" );
            return -2;
        }
        XrdDMLogInfo ( "Renewing AliEn token..." );
        if ( TokenInit ( showProcess ) ) {
            XrdDMLogInfo ( "AliEn token renewed successfully" );
        } else {
            XrdDMLogError ( "Cannot renew AliEn token" );
            return -1;
        }

    } else if ( !IsTokenValid(showProcess) ) {
        XrdDMLogInfo ( "Renewing AliEn token..." );
        XrdDMLogGInfo ( "Renewing AliEn token..." );
        if ( TokenInit ( showProcess ) ) {
            XrdDMLogInfo ( "AliEn token renewed successfully" );
        } else {
            XrdDMLogError ( "Cannot renew AliEn token" );
            return -1;
        }
    } else {
        XrdDMLogInfo ( "Reusing existing proxy" );
        XrdDMLogInfo ( "Reusing existing token" );
    }
    return 0;
}

bool XrdDownloadManagerAliEn::IsTokenValid(bool /*showProcess*/) {

    if ( fAliEnTokenInfoCmd.empty() ) {
        XrdDMLogGFatal ( "Command alien-token-info was not found at compilation time !!!" );
        XrdDMLogFatal ( "Command alien-token-info was not found at compilation time !!!" );
        return false;
    }

    string cmd;
    string redirectTo;

    if ( !fCurrentGlobalLogFileName.empty() ) {
        redirectTo.append ( ">> " );
        redirectTo.append ( fCurrentGlobalLogFileName.c_str() );
        redirectTo.append ( " 2>&1 " );
    }
    cmd = fAliEnTokenInfoCmd.c_str();
    cmd.append(" ");
    cmd.append(fAliEnUserName.c_str());
    cmd.append(" > /dev/null 2>&1");
    
    XrdDMLogDebugFull ( "Running %s",cmd.c_str() );

    if ( system ( cmd.c_str() ) ) {
        XrdDMLogGWarning ( "AliEn token expired or not found !!!" );
        XrdDMLogWarning ( "AliEn token expired or not found !!!" );
        return false;
    }

    return true;
}

bool XrdDownloadManagerAliEn::TokenInit ( bool showProcess ) {

    if ( fAliEnTokenInitCmd.empty() ) {
        XrdDMLogGFatal ( "Command alien-token-init was not found at compilation time !!!" );
        XrdDMLogFatal ( "Command alien-token-init was not found at compilation time !!!" );
        return false;
    }
    string cmd;
    string redirectTo;

    if ( !fCurrentGlobalLogFileName.empty() ) {
        redirectTo.append ( ">> " );
        redirectTo.append ( fCurrentGlobalLogFileName.c_str() );
        redirectTo.append ( " 2>&1 " );
    }

    cmd = fAliEnTokenInitCmd.c_str();
    cmd.append(" ");
    cmd.append(fAliEnUserName.c_str());
    cmd.append(" ");
    cmd.append(( showProcess ? redirectTo.c_str() : "> /dev/null 2>&1" ));

    XrdDMLogDebugFull ( "Sleeping 2 sec ..." );
    sleep ( 2 );
    XrdDMLogDebugFull ( "Running %s",cmd.c_str() );

    if ( system ( cmd.c_str() ) ) {
        XrdDMLogGError ( "alien-token-init returned non zero value !!!" );
        XrdDMLogError ( "alien-token-init returned non zero value !!!" );
        return false;
    }
    return true;
}

bool XrdDownloadManagerAliEn::TokenDestroy ( bool showProcess ) {
    if ( fAliEnTokenDestroyCmd.empty() ) {
        XrdDMLogGFatal ( "Command alien-token-destroy was not found at compilation time !!!" );
        XrdDMLogFatal ( "Command alien-token-destroy was not found at compilation time !!!" );
        return false;
    }

    string cmd;
    string redirectTo;

    if ( !fCurrentGlobalLogFileName.empty() ) {
        redirectTo.append ( ">> " );
        redirectTo.append ( fCurrentGlobalLogFileName.c_str() );
        redirectTo.append ( " 2>&1 " );
    }

    cmd = fAliEnTokenDestroyCmd.c_str();
    cmd.append(" ");
    cmd.append(( showProcess ? redirectTo.c_str() : "> /dev/null 2>&1" ));

    XrdDMLogDebugFull ( "Running %s",cmd.c_str() );
    if ( system ( cmd.c_str() ) ) {
        XrdDMLogGWarning ( "alien-token-destroy returned non zero value !!!" );
        XrdDMLogWarning ( "alien-token-destroy returned non zero value !!!" );
        return false;
    }
    return true;
}


bool XrdDownloadManagerAliEn::IsProxyValid(bool showProcess) {
  if ( fXrdgsiproxyCmd.empty() ) {
    XrdDMLogGFatal ( "Command xrdgsiproxy was not found at compilation time !!!" );
    XrdDMLogFatal ( "Command xrdgsiproxy was not found at compilation time !!!" );
    return false;
  }
  
  string cmd;
  string redirectTo;
  if ( !fCurrentGlobalLogFileName.empty() ) {
    redirectTo.append ( ">> " );
    redirectTo.append ( fCurrentGlobalLogFileName.c_str() );
    redirectTo.append ( " 2>&1 " );
  }
  
  cmd = fXrdgsiproxyCmd.c_str();
  cmd.append(" -e -valid ");
  cmd.append(fRenewCertificateTime.c_str());
    
    cmd.append(( showProcess ? redirectTo.c_str() : " > /dev/null 2>&1" ));
    
    XrdDMLogDebugFull ( "Running %s",cmd.c_str() );
    if ( system ( cmd.c_str() ) ) {
      XrdDMLogGError ( "xrdgsiproxy returned non zero value !!!" );
      XrdDMLogError ( "xrdgsiproxy returned non zero value !!!" );
      return false;
    }
    
    return true;
}

bool XrdDownloadManagerAliEn::ProxyInit ( bool showProcess ) {

    if ( fXrdgsiproxyCmd.empty() ) {
        XrdDMLogGFatal ( "Command xrdgsiproxy was not found at compilation time !!!" );
        XrdDMLogFatal ( "Command xrdgsiproxy was not found at compilation time !!!" );
        return false;
    }

    string cmd;
    string redirectTo;
    if ( !fCurrentGlobalLogFileName.empty() ) {
        redirectTo.append ( ">> " );
        redirectTo.append ( fCurrentGlobalLogFileName.c_str() );
        redirectTo.append ( " 2>&1 " );
    }

cmd = fXrdgsiproxyCmd.c_str();
cmd.append(" init" );

if ( !fCertificateDir.empty() ) {
  cmd.append(" -cert " );
  cmd.append(fCertificateDir.c_str() );
  cmd.append("/usercert.pem" );
  cmd.append(" -key " );
  cmd.append(fCertificateDir.c_str() );
  cmd.append("/userkey.pem" );
}

cmd.append(( showProcess ? redirectTo.c_str() : " > /dev/null 2>&1" ));

    XrdDMLogDebugFull ( "Running %s",cmd.c_str() );
    XrdDMLogGInfo ( "Running %s",cmd.c_str() );
    if ( system ( cmd.c_str() ) ) {
        XrdDMLogGError ( "xrdgsiproxy returned non zero value !!!" );
        XrdDMLogError ( "xrdgsiproxy returned non zero value !!!" );
        return false;
    }

    return true;
}

int XrdDownloadManagerAliEn::DoSetAliEnVariables() {


    XrdDMLogDebugFull ( "DoSetAliEnVariables()" );
    string filename ( "/tmp/gclient_env_" );
    char uid[10];
    sprintf ( uid, "%u", getuid() );
    filename.append ( uid );
    XrdDMLogDebugFull ( "DoSetAliEnVariables %s",filename.c_str() );

    fstream file ( filename.c_str(), ios::in );
    if ( !file.is_open() ) {
        XrdDMLogGWarning ( "File %s could not be opened !!! Probably alien-token was destroyed ...", filename.c_str() );
        XrdDMLogWarning ( "File %s could not be opened !!! Probably alien-token was destroyed ...", filename.c_str() );
        return -1;
    }
    size_t found;
    string str="", var="", val="";
    while ( file >> str ) {

        found = str.find ( "export" );
        if ( found != string::npos ) {
            file >> str;
            found = str.find_first_of ( "=" );
            var = str.substr ( 0, found );
            val = str.substr ( found + 1, str.length() );
            found = val.find ( "\"" );
            if ( found != string::npos ) {
                val.replace ( 0, 1, "" );
                val.replace ( val.length() - 1, 1, "" );
            }
            found = var.find ( "LD_LIBRARY_PATH" );
            if ( ! ( found != string::npos ) ) {
                setenv ( var.c_str(), val.c_str(), 1 );
            }
            XrdDMLogDebugFull ( "DoSetAliEnVariables %s %s",var.c_str(), val.c_str() );
        }

    }
    file.close();
//     system("export");
    return 0;
}

// int XrdDownloadManagerAliEn::ReadConfigFile ( string filename ) {
//     XrdDMLogDebugMedium( "AliEn ReadConfigFile : Config file is %s",filename.c_str() );
//
//     return XrdDownloadManagerBase::ReadConfigFile (filename );
// }

int XrdDownloadManagerAliEn::SetConfigOptions ( string var, string val ) {

    // TODO handle ALIEN part

    if ( var.compare ( "xrddm.AliEnUserName" ) == 0 ) {
        XrdDMLogDebugMedium ( "SetConfigOptions %s : %s",var.c_str(),val.c_str() );
        fAliEnUserName =  val ;
    } else if ( var.compare ( "xrddm.RenewCertificateTimeHHMM" ) == 0 ) {
        XrdDMLogDebugMedium ( "SetConfigOptions %s : %s",var.c_str(),val.c_str() );
        fRenewCertificateTime =  val.c_str() ;
    } else if ( var.compare ( "xrddm.CertificateDir" ) == 0 ) {
        XrdDMLogDebugMedium ( "SetConfigOptions %s : %s",var.c_str(),val.c_str() );
        SetCertificateDir ( val.c_str() ) ;
    }

    return XrdDownloadManagerBase::SetConfigOptions ( var, val );
}


int XrdDownloadManagerAliEn::DoAliEnCp ( string from, string to ) {
    if ( fAliEnCpCmd.empty() ) {
        XrdDMLogGFatal ( "Command alien_cp was not found at compilation time !!!" );
        XrdDMLogFatal ( "Command alien_cp was not found at compilation time !!!" );
        return 1;
    }

//     char str[512];
    string redirectTo;
    if ( ( !fCurrentLogFileName.empty() ) && ( fLogDM ) ) {
        redirectTo.append ( ">> " );
        redirectTo.append ( fCurrentLogFileName.c_str() );
        redirectTo.append ( " 2>&1 " );
    }

    string alienfilenametmp = GetXrdFilename ( from.c_str() );

    XrdDMLogDebugSilent ( "Doing alien_cp -v alien://%s file:%s %s ",GetXrdFilename ( fFileNameFrom ).c_str(),to.c_str(),redirectTo.c_str() );
    XrdDMLogGInfo ( "xrddm [%s] aliencp alien://%s %s",fFileNameFrom.c_str(),GetXrdFilename ( fFileNameFrom ).c_str(),redirectTo.c_str() );
    XrdDMLogInfo ( "aliecp alien://%s",GetXrdFilename ( fFileNameFrom ).c_str() );
    string str=fAliEnCpCmd.c_str();
    str.append ( " -v alien://" );
    str.append ( GetXrdFilename ( fFileNameFrom ).c_str() );
    str.append ( " file:" );
    str.append ( to.c_str() );
    str.append ( " " );
    str.append ( redirectTo.c_str() );
//     sprintf ( str,"%s -v alien://%s file:%s %s",fAliEnCpCmd.c_str(),GetXrdFilename ( fFileNameFrom ).c_str(),to.c_str(),redirectTo.c_str() );
    int retValAlienCp = system ( str.c_str() );
    XrdDMLogDebugSilent ( "alien_cp : retValAlienCp %d",retValAlienCp );

    // testing zip after download
    int retValAlienCpZip = TestZipFile ( from.c_str(),to.c_str() );

    if ( ( !retValAlienCp ) && ( !retValAlienCpZip ) )
        return 0;
    return 1;
}

XrdDownloadManagerBase::EInputTypes_t XrdDownloadManagerAliEn::GetInputTypeId ( string inputTypeString ) {

    if ( !strcmp ( inputTypeString.c_str(),"alien" ) ) {
        return kStdXrdcpAliEnWhereis;
    }  else if ( !strcmp ( inputTypeString.c_str(),"aliengr" ) ) {
        return kStdXrdcpAliEnWhereisGR;
    } else if ( !strcmp ( inputTypeString.c_str(),"aliencp" ) ) {
        return kStdAliEnCp;
    }

    return XrdDownloadManagerBase::GetInputTypeId ( inputTypeString );
}


// void XrdDownloadManagerAliEn::FindAndReplaceString ( string &source, const string find, string replace ) {
//
//     size_t j;
//     for ( ; ( j = source.find ( find ) ) != string::npos ; ) {
//         source.replace ( j, find.length(), replace );
//     }
// }
// kate: indent-mode cstyle; space-indent on; indent-width 4;

void XrdDownloadManagerAliEn::CallAliEnTimeout ( int i ) {

    alarm ( 0 );
    throw 100;


    if ( i ) return;
}

